
#include <Archaism/DataSet.h>
#include <Archaism/String.h>

_BeginNamespace(Radix)
/*
============================================================================================
==== DataCell
============================================================================================
*/
DataCell::DataCell()
	:mLength(0),
	mData(nullptr)
{}

DataCell::~DataCell()
{
	this->clear();
}

uchar* DataCell::data() const
{
	return mData;
}

ushort DataCell::length() const
{
	return mLength;
}

void DataCell::setData(uchar* data, ushort length)
{
	this->clear();
	mData = new uchar[length];
	memcpy(mData, data, length);
	mLength = length;
}

void DataCell::clear()
{
	mLength = 0;
	if(mData != nullptr)
	{
		delete []mData;
		mData = nullptr;
	}
}

DataCell& DataCell::operator=(const DataCell& cell)
{
	if(this != &cell)
	{
		this->setData(cell.mData, cell.mLength);
	}
	return *this;
}
DataCell& DataCell::operator=(const String& value)
{
	std::string utf8 = StringUtil::toUtf8(value);
	this->setData((uchar*)utf8.c_str(), (ushort)utf8.size());
	return *this;
}
DataCell& DataCell::operator=(char value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(uchar value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(short value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(ushort value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(int value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(uint value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(long value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(ulong value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(longlong value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(ulonglong value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(float value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(double value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(longdouble value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}
DataCell& DataCell::operator=(bool value)
{
	this->setData((uchar*)&value, sizeof(value));
	return *this;
}

DataCell::operator String()
{
	char* cstr = new char[mLength+1];
	memcpy(cstr, mData, mLength);
	cstr[mLength] = '\0';
	return StringUtil::fromUtf8(cstr);
}
DataCell::operator char()
{
	return *(char*)mData;
}
DataCell::operator uchar()
{
	return *(uchar*)mData;
}
DataCell::operator short()
{
	return *(short*)mData;
}
DataCell::operator ushort()
{
	return *(ushort*)mData;
}
DataCell::operator int()
{
	return *(int*)mData;
}
DataCell::operator uint()
{
	return *(uint*)mData;
}
DataCell::operator long()
{
	return *(long*)mData;
}
DataCell::operator ulong()
{
	return *(ulong*)mData;
}
DataCell::operator longlong()
{
	return *(longlong*)mData;
}
DataCell::operator ulonglong()
{
	return *(ulonglong*)mData;
}
DataCell::operator float()
{
	return *(float*)mData;
}
DataCell::operator double()
{
	return *(double*)mData;
}
DataCell::operator longdouble()
{
	return *(longdouble*)mData;
}
DataCell::operator bool()
{
	return *(bool*)mData;
}
/*
============================================================================================
==== DataRow
============================================================================================
*/
DataRow::DataRow()
	:mCells()
{}

DataRow::~DataRow()
{}

DataCell& DataRow::operator[](const String& colName)
{
	return *(this->getCell(colName));
}

void DataRow::setCell(const String& colName, DataCell* cell)
{
	mCells[colName] = cell;
}

DataCell* DataRow::getCell(const String& colName)
{
	CellMap::iterator cellIter = mCells.find(colName);
	if(cellIter == mCells.end())
		return nullptr;
	return cellIter->second;
}
/*
============================================================================================
==== DataCol
============================================================================================
*/
DataCol::DataCol(const String& name)
	:mName(name),
	mComm(),
	mType(eDataType_Raw),
	mLength(0),
	mCells()
{}

DataCol::~DataCol()
{
	this->clearCells();
}

const String& DataCol::name() const
{
	return mName;
}

const String& DataCol::comm() const
{
	return mComm;
}

ushort DataCol::length() const
{
	return mLength;
}

const DataType& DataCol::type() const
{
	return mType;
}

void DataCol::setComm(const String& comm)
{
	mComm = comm;
}

void DataCol::setLength(ushort length)
{
	mLength = length;
}

void DataCol::setType(const DataType& type)
{
	mType = type;
}

DataCell* DataCol::createCell()
{
	DataCell* cell = new DataCell();
	mCells.push_back(cell);
	return cell;
}

DataCell* DataCol::selectCell(uint rowId)
{
	if(rowId >= mCells.size())
		return nullptr;
	return mCells[rowId];
}

void DataCol::deleteCell(uint rowId)
{
	if(rowId >= mCells.size())
		return;
	if(rowId == 0)
	{
		DataCell* cell = mCells.front();
		if(cell != nullptr)
		{
			delete cell;
		}
		mCells.pop_front();
	}
	else if(rowId == mCells.size())
	{
		DataCell* cell = mCells.back();
		if(cell != nullptr)
		{
			delete cell;
		}
		mCells.pop_back();
	}
	else if(rowId < mCells.size() / 2)
	{
		CellDeque::iterator cellIter = mCells.begin();
		for(uint i = 0; i < rowId; ++i)
		{
			++ cellIter;
		}
		DataCell* cell = *cellIter;
		if(cell != nullptr)
		{
			delete cell;
		}
		mCells.erase(cellIter);
	}
	else
	{
		CellDeque::iterator cellIter = mCells.end();
		for(uint i = mCells.size(); i > rowId; --i)
		{
			-- cellIter;
		}
		DataCell* cell = *cellIter;
		if(cell != nullptr)
		{
			delete cell;
		}
		mCells.erase(cellIter);
	}
}

void DataCol::clearCells()
{
	CellDeque::iterator cellIter = mCells.begin();
	while(cellIter != mCells.end())
	{
		DataCell* cell = *cellIter;
		if(cell != nullptr)
		{
			delete cell;
		}
		++ cellIter;
	}
	mCells.clear();
}


/*
============================================================================================
==== DataTable
============================================================================================
*/
DataTable::DataTable(const String& name)
	:mName(name),
	mComm(),
	mCols(),
	mRowCount(0)
{}

DataTable::~DataTable()
{
	ColMap::iterator colIter = mCols.begin();
	while(colIter != mCols.end())
	{
		DataCol* col = colIter->second;
		if(col != nullptr)
		{
			delete col;
		}
		++ colIter;
	}
	mCols.clear();
}

DataCol& DataTable::operator[](const String& colName)
{
	if(this->containsCol(colName))
	{
		return *(this->selectCol(colName));
	}
	return *(this->createCol(colName));
}

DataRow DataTable::operator[](uint rowId)
{
	return this->selectRow(rowId);
}

bool DataTable::containsCol(const String& colName)
{
	return mCols.find(colName) != mCols.end();
}

const String& DataTable::name() const
{
	return mName;
}

const String& DataTable::comm() const
{
	return mComm;
}

void DataTable::setComm(const String& comm)
{
	mComm = comm;
}

uint DataTable::colCount() const
{
	return mCols.size();
}

uint DataTable::rowCount() const
{
	return mRowCount;
}

DataCol* DataTable::createCol(const String& colName)
{
	if(mCols.find(colName) != mCols.end())
		return nullptr;
	DataCol* col = new DataCol(colName);
	mCols[colName] = col;
	return col;
}

DataCol* DataTable::selectCol(const String& colName)
{
	ColMap::iterator colIter = mCols.find(colName);
	if(colIter == mCols.end())
		return nullptr;
	return colIter->second;
}

void DataTable::deleteCol(const String& colName)
{
	ColMap::iterator colIter = mCols.find(colName);
	if(colIter != mCols.end())
	{
		DataCol* col = colIter->second;
		if(col != nullptr)
		{
			delete col;
		}
		mCols.erase(colIter);
	}
}

DataRow DataTable::createRow()
{
	DataRow row;
	ColMap::iterator colIter = mCols.begin();
	while(colIter != mCols.end())
	{
		DataCol* col = colIter->second;
		if(col != nullptr)
		{
			DataCell* cell = col->createCell();
			row.setCell(col->name(), cell);
		}

		++ colIter;
	}
	mRowCount += 1;
	return row;
}

DataRow DataTable::selectRow(uint rowId)
{
	DataRow row;
	if(rowId < mRowCount)
	{
		ColMap::iterator colIter = mCols.begin();
		while(colIter != mCols.end())
		{
			DataCol* col = colIter->second;
			if(col != nullptr)
			{
				DataCell* cell = col->selectCell(rowId);
				row.setCell(col->name(), cell);
			}

			++ colIter;
		}
	}
	return row;
}

void DataTable::deleteRow(uint rowId)
{
	if(rowId < mRowCount)
	{
		ColMap::iterator colIter = mCols.begin();
		while(colIter != mCols.end())
		{
			DataCol* col = colIter->second;
			if(col != nullptr)
			{
				col->deleteCell(rowId);
			}

			++ colIter;
		}
		mRowCount -= 1;
	}
}

void DataTable::clear()
{
	ColMap::iterator colIter = mCols.begin();
	while(colIter != mCols.end())
	{
		DataCol* col = colIter->second;
		if(col != nullptr)
		{
			col->clearCells();
		}

		++ colIter;
	}
	mRowCount = 0;
}
/*
============================================================================================
==== DataSet
============================================================================================
*/
DataSet::DataSet()
	:mVersion(_RadixDataSetVersion),
	mTables()
{}

DataSet::~DataSet()
{
	TableMap::iterator tableIter = mTables.begin();
	while(tableIter != mTables.end())
	{
		DataTable* table = tableIter->second;
		if(table != nullptr)
		{
			delete table;
		}
		++ tableIter;
	}
	mTables.clear();
}

DataTable& DataSet::operator[](const String& tableName)
{
	if(this->containsTable(tableName))
	{
		return *(this->selectTable(tableName));
	}
	return *(this->createTable(tableName));
}

uint DataSet::tableCount() const
{
	return mTables.size();
}

bool DataSet::containsTable(const String& tableName)
{
	return mTables.find(tableName) != mTables.end();
}

DataTable* DataSet::createTable(const String& tableName)
{
	if(mTables.find(tableName) != mTables.end())
		return nullptr;
	DataTable* table = new DataTable(tableName);
	mTables[tableName] = table;
	return table;
}

DataTable* DataSet::selectTable(const String& tableName)
{
	TableMap::iterator tableIter = mTables.find(tableName);
	if(tableIter == mTables.end())
		return nullptr;
	return tableIter->second;
}

void DataSet::deleteTable(const String& tableName)
{
	TableMap::iterator tableIter = mTables.find(tableName);
	if(tableIter != mTables.end())
	{
		DataTable* table = tableIter->second;
		if(table != nullptr)
		{
			delete table;
		}
		mTables.erase(tableIter);
	}
}

void DataSet::load(uchar* bytes)
{
	if(bytes == nullptr)
		return;

	uchar* ptr = bytes;

	mVersion = *((uint*)ptr);
	ptr += 4;

	ushort tableCount = *((ushort*)ptr);
	ptr += 2;
	for(ushort tableId = 0; tableId < tableCount; tableId ++)
	{
		ushort tableNameLength = *((ushort*)ptr);
		ptr += 2;
		char* tableNameBuffer = new char[tableNameLength+1];
		memcpy(tableNameBuffer, ptr, tableNameLength);
		tableNameBuffer[tableNameLength] = '\0';
		String tableName = StringUtil::fromUtf8(tableNameBuffer);
		delete []tableNameBuffer;
		ptr += tableNameLength;

		ushort tableCommLength = *((ushort*)ptr);
		ptr += 2;
		char* tableCommBuffer = new char[tableCommLength+1];
		memcpy(tableCommBuffer, ptr, tableCommLength);
		tableCommBuffer[tableCommLength] = '\0';
		String tableComm = StringUtil::fromUtf8(tableCommBuffer);
		delete []tableCommBuffer;
		ptr += tableCommLength;

		DataTable* table = this->createTable(tableName);
		table->setComm(tableComm);

		ushort colCount = *((ushort*)ptr);
		ptr += 2;
		ushort rowCount = *((ushort*)ptr);
		ptr += 2;

		table->mRowCount = rowCount;
		for(ushort colId = 0; colId < colCount; colId ++)
		{
			ushort colNameLength = *((ushort*)ptr);
			ptr += 2;
			char* colNameBuffer = new char[colNameLength+1];
			memcpy(colNameBuffer, ptr, colNameLength);
			colNameBuffer[colNameLength] = '\0';
			String colName = StringUtil::fromUtf8(colNameBuffer);
			delete []colNameBuffer;
			ptr += colNameLength;

			ushort colCommLength = *((ushort*)ptr);
			ptr += 2;
			char* colCommBuffer = new char[colCommLength+1];
			memcpy(colCommBuffer, ptr, colCommLength);
			colCommBuffer[colCommLength] = '\0';
			String colComm = StringUtil::fromUtf8(colCommBuffer);
			delete []colCommBuffer;
			ptr += colCommLength;

			ushort colLength = *((ushort*)ptr);
			ptr += 2;
			ushort colType = *((ushort*)ptr);
			ptr += 2;
			
			DataCol* col = table->createCol(colName);
			col->setComm(colComm);
			col->setLength(colLength);
			col->setType((DataType)colType);

			for(ushort rowId = 0; rowId < rowCount; rowId ++)
			{
				ushort cellLength = *((ushort*)ptr);
				ptr += 2;
				uchar* cellData = new uchar[cellLength];
				memcpy(cellData, ptr, cellLength);
				ptr += cellLength;

				DataCell* cell = col->createCell();
				cell->setData(cellData, cellLength);
			}
		}
	}
}

void DataSet::save(uchar* bytes, uint* length)
{
	if(bytes == nullptr)
		return;

	uchar* ptr = bytes;
	
	(*(uint*)ptr) = mVersion;
	ptr += 4;

	(*(ushort*)ptr) = (ushort)this->tableCount();
	ptr += 2;
	TableMap::iterator tableIter = mTables.begin();
	while(tableIter != mTables.end())
	{
		DataTable* table = tableIter->second;
		if(table != nullptr)
		{
			std::string tableName = StringUtil::toUtf8(table->name());
			(*(ushort*)ptr) = (ushort)tableName.size();
			ptr += 2;
			memcpy(ptr, tableName.c_str(), tableName.size());
			ptr += tableName.size();

			std::string tableComm = StringUtil::toUtf8(table->comm());
			(*(ushort*)ptr) = (ushort)tableComm.size();
			ptr += 2;
			memcpy(ptr, tableComm.c_str(), tableComm.size());
			ptr += tableComm.size();

			(*(ushort*)ptr) = (ushort)table->colCount();;
			ptr += 2;
			(*(ushort*)ptr) = (ushort)table->rowCount();
			ptr += 2;

			DataTable::ColMap::iterator colIter = table->mCols.begin();
			while(colIter != table->mCols.end())
			{
				DataCol* col = colIter->second;
				if(col != nullptr)
				{
					std::string colName = StringUtil::toUtf8(col->name());
					(*(ushort*)ptr) = (ushort)colName.size();
					ptr += 2;
					memcpy(ptr, colName.c_str(), colName.size());
					ptr += colName.size();

					std::string colComm = StringUtil::toUtf8(col->comm());
					(*(ushort*)ptr) = (ushort)colComm.size();
					ptr += 2;
					memcpy(ptr, colComm.c_str(), colComm.size());
					ptr += colComm.size();

					(*(ushort*)ptr) = (ushort)col->length();
					ptr += 2;
					(*(ushort*)ptr) = (ushort)col->type();
					ptr += 2;

					for(ushort rowId = 0; rowId < table->rowCount(); rowId ++)
					{
						DataCell* cell = col->mCells[rowId];
						if(cell != nullptr)
						{
							(*(ushort*)ptr) = (ushort)cell->length();
							ptr += 2;
							memcpy(ptr, cell->data(), cell->length());
							ptr += cell->length();
						}
						else
						{
							(*(ushort*)ptr) = 0;
							ptr += 2;
						}
					}
				}
				++ colIter;
			}
		}
		++ tableIter;
	}
	*length = ptr - bytes;
}

void DataSet::clear()
{
	TableMap::iterator tableIter = mTables.begin();
	while(tableIter != mTables.end())
	{
		DataTable* table = tableIter->second;
		if(table == nullptr)
		{
			tableIter = mTables.erase(tableIter);
			continue;
		}
		table->clear();

		++ tableIter;
	}
}

_EndNamespace(Radix)
